import re
import pandas as pd
import xml.etree.ElementTree as ET
import json
from numpy import nan
from 获取CWE编号的继承关系 import get_cwe_root
from datetime import time
from datetime import datetime
import os
from pycparser import c_parser,c_ast

class JULIET:
    '''解析juliet的manifest.xml文件,生成结果'''
    def __init__(self) -> None:
        self.mainfest_xml_path='/home/pudding/work/temp/manifest.xml'
        self.result={}
        self.generate_result()

    def generate_result(self):
        '''解析xml文件生成result结果'''
        tree=ET.parse(self.mainfest_xml_path)
        root=tree.getroot()
        testcases=root.findall('testcase')
        for testcase in testcases:
            files=testcase.findall('file')
            one_testcase=[]
            for file in files:
                file_name=file.attrib['path']
                file_cweid='CWE-'+re.findall(r'CWE(\d+)_',file_name,flags=0)[0]
                if  not self.result.get(file_cweid):
                    self.result[file_cweid]=[]
                one_file_info={'file_name':file_name,'error_info':[]}
                flaws=file.findall('flaw')
                for flaw in flaws:
                    flaw_line=flaw.attrib['line']
                    flaw_cweid=re.findall(r'^(CWE-\d+)',flaw.attrib['name'],flags=0)[0]
                    one_file_info['error_info'].append({'line':flaw_line,'cweid':flaw_cweid})
                one_testcase.append(one_file_info)
            self.result[file_cweid].append(one_testcase)
    
    def get_testcase_by_file_name(self,file_name):
        '''根据文件名获取对应flaw信息,如果没有该文件名相关信息返回None'''
        if not re.findall(r'CWE(\d+)',file_name,flags=0):
            return None
        cweid='CWE-'+re.findall(r'CWE(\d+)',file_name,flags=0)[0]
        testcases=self.result[cweid]
        for testcase in testcases:
            for file in testcase:
                if file['file_name']==file_name:
                    return testcase
                else:
                    continue
        return None
    def get_testcase_cweid(self,testcase):
        '''返回对应testcase的cweid'''
        cweid='CWE-'+re.findall(r'CWE(\d+)',testcase[0]['file_name'],flags=0)[0]
        return cweid

    def if_mate_line(self,line,file_name,juliet_testcase):
        '''检查testcase中对应的file,行数是否匹配,匹配成功返回True,失败返回False'''
        for file in juliet_testcase:
            if file['file_name']==file_name:
                for error in file['error_info']:
                    if error['line']==line:
                        return True
        return False
    
    def if_bad_function_name(self,function_name):
        '''判断当前函数是否是bad函数'''
        if re.match(r'^(.*)?bad$',function_name):
            return True
        else:
            return False

class INFER:
    '''对infer的report.json进行分析'''
    def __init__(self) -> None:
        # infer结果json文件路径
        self.infer_result_json_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/report.json'
        # infer的结果与cwe编号对应关系的excel文件路径
        self.juliet_cwe_and_infer_excel_path='/home/pudding/work/temp/juliet_cwe_and_infer.xlsx'
        # infer的结果与cwe编号对应关系的excel文件的sheet表名
        self.juliet_cwe_and_infer_excel_sheetname='cwe'
        # 根据juliet_cwe_and_infer_excel_path来生成的，将bug_type与cweid对应
        self.bug_type_to_cweid_dict={}
        # 根据infer_result_json_path来生成的，保存了infer的分析结果
        self.result=[]
        # 生成result数据
        self.analyze_result=[]
        # juliet对象，里面包括了juliet数据集的预期数据及一些可以调用的方法
        self.juliet=JULIET()
        self.get_result_from_json_file()
        self.get_bug_type_to_cweid_dict()
        self.get_analyze_result()
        # 性能分析
        time_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_time.log'
        top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_top.log'
        iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_iotop.log'
        top_command='infer'
        iotop_command='infer run --bufferoverrun --biabduction --pulse -- make -j6 -B'
        performance_save_path='./analyze_result/infer_performance.xlsx'
        performance=PERFORMANCE(time_log_path,top_log_path,iotop_log_path,top_command,iotop_command,performance_save_path)

    def get_result_from_json_file(self):
        '''将infer分析出的json结果解析到result对象中'''
        with open(self.infer_result_json_path,'r') as f:
            infer_json_file_content=json.loads(f.read())
        for item in infer_json_file_content:
            one_result={
                'bug_type':item['bug_type'],
                'line':str(item['line']),
                'procedure':item['procedure'],
                'file':item['file']
            }
            self.result.append(one_result)
    
    def get_bug_type_to_cweid_dict(self):
        '''将bug_type字段解析为cweid'''
        df=pd.read_excel(self.juliet_cwe_and_infer_excel_path,sheet_name=self.juliet_cwe_and_infer_excel_sheetname)
        for i in range(len(df['cweid'])):
            cweid=df['cweid'][i]
            bug_types=df['bug_types'][i]
            if pd.isnull(bug_types):
                continue
            for bug_type in bug_types.split(' '):
                if not self.bug_type_to_cweid_dict.get(bug_type):
                    self.bug_type_to_cweid_dict[bug_type]=[cweid]
                else:
                    self.bug_type_to_cweid_dict[bug_type].append(cweid)

    def get_analyze_result(self):
        '''对结果进行分析'''
        # juliet测试用例的总数
        juliet_testcases_number=0
        for item in self.juliet.result.values():
            juliet_testcases_number+=len(item)
        # infer重复报告相同的juliet测试用例数量
        infer_repeat_number=0
        # infer的结果总数
        infer_result_number=len(self.result)
        # 匹配成功的用例数量
        success_report_number=0
        # 匹配成功的juliet的测试用例列表,避免同一个测试用例被多次算作success_report_number里
        success_juliet_testcase_list=[]
        # 初始化cweid_success_number_dict，用于记录各cwe编号成功测试用例数
        # for item in self.juliet.result.keys():
        #     self.cweid_success_number_dict[item]=0
        #     self.cweid_error_number_dict[item]=0
        # 遍历infer的result
        for item in self.result:
            # 如果是与juliet测试用例中的cwe编号匹配的bug_type
            if item['bug_type'] in self.bug_type_to_cweid_dict.keys():
                cweid_list=self.bug_type_to_cweid_dict[item['bug_type']]
                file_name=item['file'].split('/')[-1]
                # 根据文件名对应到juliet里的一个测试用例
                juliet_testcase=self.juliet.get_testcase_by_file_name(file_name)
                # 如果找不到该文件名
                if not juliet_testcase:
                    continue
                juliet_testcase_cweid=self.juliet.get_testcase_cweid(juliet_testcase)
                # 如果juliet测试用例的cweid在bug_type对应的cweid_list中
                if juliet_testcase_cweid in cweid_list:
                    # 如果行数匹配,判断为一次成功报告,并记录到success_juliet_testcase_list中防止后续被重复计算
                    if self.juliet.if_mate_line(item['line'],file_name,juliet_testcase):
                        # 判断该juliet的测试用例如果之前有没有被统计过
                        if juliet_testcase not in success_juliet_testcase_list:
                            success_report_number+=1
                            success_juliet_testcase_list.append(juliet_testcase)
                            continue
                        else:
                            infer_repeat_number+=1
                    # 行数不匹配的情况下,判断当前报告的函数是否为bad函数
                    else:
                        if self.juliet.if_bad_function_name(item['procedure']):
                            # 判断该juliet的测试用例如果之前有没有被统计过
                            if juliet_testcase not in success_juliet_testcase_list:
                                success_report_number+=1
                                success_juliet_testcase_list.append(juliet_testcase)
                                continue
                            else:
                                infer_repeat_number+=1

        # 召回率/准确率：正确报告数与测试用例中用例总数的比值
        rcr=success_report_number/juliet_testcases_number
        # 误报率：错误报告数占工具所有报告总数的比值，错误报告数=infer总报告数-infer成功报告数-infer重复报告数
        fpr=(infer_result_number-success_report_number-infer_repeat_number)/infer_result_number
        excel_res={
            'juliet_testcases_number':[juliet_testcases_number],
            'infer_result_number':[infer_result_number],
            'success_report_number':[success_report_number],
            'rcr':[rcr],
            'fpr':[fpr],
        }
        df=pd.DataFrame(excel_res)
        df.to_excel('./analyze_result/infer_analyze_result.xlsx', sheet_name='main')

class CODEQL:
    '''对codeql的report.json进行分析'''
    def __init__(self) -> None:
        # codeql结果的csv文件路径
        self.codeql_result_csv_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/result.csv'
        # 存储了codeql解析结果name字段与cweid对应关系的excel文件路径
        self.codeql_cwe_name_excel_path='/home/pudding/work/temp/codeql_cwe_name.xlsx'
        # 存储了codeql解析结果name字段与cweid对应关系的excel文件的sheet表名字
        self.codeql_cwe_name_excel_sheetname='cwe'
        # 根据codeql_result_csv_path生成的，用于存储codeql的结果
        self.result=[]
        # name与cweid对应关系字典
        self.name_cweid_dict={}
        # 用于获取cwe编号之间继承关系的对象
        self.cwe_root=get_cwe_root()
        # juliet对象,为juliet测试集和一些函数
        self.juliet=JULIET()
        self.get_name_cweid_dict()
        self.get_result_from_csv_file()
        self.analyze_result()
        # 性能分析
        self.time_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_time.log'
        self.create_time_log_path='codeql_create_time.log'
        self.analyze_time_log_path='codeql_analyze_time.log'
        self.performance_save_path='./analyze_result/codeql_performance.xlsx'
        # 性能分析-create
        self.create_top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_create_database_top.log'
        self.create_iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_create_database_iotop.log'
        self.create_top_command='java'
        self.create_iotop_command='java'
        self.create_performance_save_path='./analyze_result/create_codeql_performance.xlsx'
        # 性能分析-analyze
        self.analyze_top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_analyze_database_top.log'
        self.analyze_iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/codeql/3/codeql_analyze_database_iotop.log'
        self.analyze_top_command='java'
        self.analyze_iotop_command='java'
        self.analyze_performance_save_path='./analyze_result/analyze_codeql_performance.xlsx'
        self.adaptation_performance()
        # 进行性能分析-create
        create_performance=PERFORMANCE(self.create_time_log_path,self.create_top_log_path,self.create_iotop_log_path,self.create_top_command,self.create_iotop_command,self.create_performance_save_path)
        # 进行性能分析-analyze
        analyze_performance=PERFORMANCE(self.analyze_time_log_path,self.analyze_top_log_path,self.analyze_iotop_log_path,self.analyze_top_command,self.analyze_iotop_command,self.analyze_performance_save_path)
        # 对codeql的create阶段和analyze阶段的数据进行汇总
        self.summary_data()
        # 善后工作,把中间过程文件删掉
        self.remove_file()

    def adaptation_performance(self):
        '''由于codeql有create和analyze两个步骤,对performance类不兼容,要进行一些适配工作'''
        with open(self.time_log_path,'r') as f:
            lines=f.readlines()
        with open(self.create_time_log_path,'w') as f:
            f.writelines(lines[0:2])
        with open(self.analyze_time_log_path,'w') as f:
            f.writelines(lines[2:4])

    def remove_file(self):
        '''善后工作,主要是删文件'''
        os.remove(self.create_time_log_path)
        os.remove(self.analyze_time_log_path)

    def summary_data(self):
        '''对analyze和create两个阶段的性能分析数据进行汇总'''
        def translate_rate_to_float(rate):
            '''把360.4KB/s转换成360400'''
            if 'GB/s' in rate:
                return float(rate[:-4])*1000*1000*1000
            if 'MB/s' in rate:
                return float(rate[:-4])*1000*1000
            if 'KB/s' in rate:
                return float(rate[:-4])*1000
            if 'B/s' in rate:
                return float(rate[:-3])

        def translate_number_to_rate(number):
            '''把6530000.0转换成6.53Mb/s'''
            if number>1000:
                if number>1000*1000:
                    if number>1000*1000*1000:
                        unit='GB/s'
                        number=number/(1000*1000*1000)
                        return str(number)+unit
                    else:
                        unit='MB/s'
                        number=number/(1000*1000)
                        return str(number)+unit
                else:
                    unit='KB/s'
                    number=number/(1000)
                    return str(number)+unit
            else:
                unit='B/s'
                number=number
                return str(number)+unit
      
        df_create=pd.read_excel(self.create_performance_save_path,sheet_name='result')
        df_analyze=pd.read_excel(self.analyze_performance_save_path,sheet_name='result')
        create_cpu=float(df_create['cpu'][0][:-1])
        create_mem=float(df_create['mem'][0][:-1])
        create_free=float(df_create['free'][0][:-1])
        create_read=translate_rate_to_float(df_create['read'][0])
        create_write=translate_rate_to_float(df_create['write'][0])
        create_time=df_create['time'][0]
        analyze_cpu=float(df_analyze['cpu'][0][:-1])
        analyze_mem=float(df_analyze['mem'][0][:-1])
        analyze_free=float(df_analyze['free'][0][:-1])
        analyze_read=translate_rate_to_float(df_analyze['read'][0])
        analyze_write=translate_rate_to_float(df_analyze['write'][0])
        analyze_time=df_analyze['time'][0]
        create_scale=create_time/(create_time+analyze_time)
        analyze_scale=analyze_time/(create_time+analyze_time)
        cpu=create_cpu*create_scale+analyze_cpu*analyze_scale
        mem=create_mem*create_scale+analyze_mem*analyze_scale
        free=create_free*create_scale+analyze_free*analyze_scale
        read=create_read*create_scale+analyze_read*analyze_scale
        write=create_write*create_scale+analyze_write*analyze_scale
        time=create_time+analyze_time
        cpu=str(cpu)+'%'
        mem=str(mem)+'%'
        free=str(free)+'%'
        read=translate_number_to_rate(read)
        write=translate_number_to_rate(write)
        res={'cpu':[cpu],'mem':[mem],'free':[free],'read':[read],'write':[write],'time':[time]}
        df=pd.DataFrame(res)
        df.to_excel(self.performance_save_path,sheet_name='result')
        pass

    def get_name_cweid_dict(self):
        '''根据codeql_cwe_name_excel_path获取name字段与cweid对应关系的字典'''
        df=pd.read_excel(self.codeql_cwe_name_excel_path,sheet_name=self.codeql_cwe_name_excel_sheetname)
        for i in range(len(df['cweid'])):
            cweid=df['cweid'][i]
            cwe_name=df['cwe_name'][i]
            self.name_cweid_dict[cwe_name]=cweid

    def get_result_from_csv_file(self):
        '''根据codeql的最终csv解析结果生成result对象'''
        df=pd.read_csv(self.codeql_result_csv_path,names=['name','description','severity','message','path','start_line','start_column','end_line','end_column'])
        for i in range(len(df['name'])):
            name=df['name'][i]
            path=df['path'][i]
            line=str(df['start_line'][i])
            cweid=self.name_cweid_dict[name]
            self.result.append({'path':path,'line':line,'cweid':cweid})
    
    def analyze_result(self):
        '''对result对象进行解析,生成最终的结果'''
        # juliet测试用例的总数
        juliet_testcases_number=0
        for item in self.juliet.result.values():
            juliet_testcases_number+=len(item)
        # 测试成功的结果数量
        success_report_number=0
        # codeql重复报告相同的juliet测试用例数量
        codeql_repeat_number=0
        # codeql的结果总数
        codeql_result_number=len(self.result)
        # 匹配成功的juliet的测试用例列表，避免同一个测试用例被多次算作success_report_number里
        success_juliet_testcase_list=[]
        testnumber=0
        # 循环遍历result
        for item in self.result:
            # 根据文件名去juliet找对应的测试用例
            file_name=item['path'].split('/')[-1]
            juliet_testcase=self.juliet.get_testcase_by_file_name(file_name)
            # 如果找不到对应的测试用例就continue
            if not juliet_testcase:
                continue
            # 获取juliet的测试用例对应的cweid
            juliet_testcase_cweid=self.juliet.get_testcase_cweid(juliet_testcase)
            # 获取codeql的cweid与juliet的cweid之间的继承关系
            relations=self.cwe_root.get_relations(item['cweid'],juliet_testcase_cweid)
            # codeql为juliet的祖辈或者相等或子辈
            if relations in ['ancestor','equal','descendant']:
                # 行数与juliet测试用例的行数匹配
                if self.juliet.if_mate_line(item['line'],file_name,juliet_testcase):
                    # 如果juliet的该测试用例没有被统计过,算一次成功的报告,成功报告数量加一，并且该测试用例记录下来
                    if juliet_testcase not in success_juliet_testcase_list:
                        success_report_number+=1
                        success_juliet_testcase_list.append(juliet_testcase)
                    # 如果该测试用例被统计过,记录到codeql_repeat_number
                    else:
                        codeql_repeat_number+=1
            # codeql与juliet之间没有辈分关系,continue
            if relations=='unrelated':
                continue
        
        # 召回率/准确率：正确报告数与测试用例中用例总数的比值
        rcr=success_report_number/juliet_testcases_number
        # 误报率：错误报告数占工具所有报告总数的比值，错误报告数=codeql总报告数-codeql成功报告数-codeql重复报告数
        fpr=(codeql_result_number-success_report_number-codeql_repeat_number)/codeql_result_number
        excel_res={
            'juliet_testcases_number':[juliet_testcases_number],
            'codeql_result_number':[codeql_result_number],
            'success_report_number':[success_report_number],
            'rcr':[rcr],
            'fpr':[fpr],
        }
        df=pd.DataFrame(excel_res)
        df.to_excel('./analyze_result/codeql_analyze_result.xlsx', sheet_name='main')

class CPPCHECK:
    '''对cppcheck的report.json进行分析'''
    def __init__(self) -> None:
        self.id_cweid_dict={'overlappingWriteFunction':'CWE-475'}
        # cppcheck结果的xml文件路径
        self.cppcheck_result_xml_path='/home/pudding/work/temp/result/sast_result/sast_result/cppcheck/3/result.xml'
        # 根据cppcheck_result_xml_path生成的，用于存储cppcheck的结果
        self.result=[]
        # 用于获取cwe编号之间继承关系的对象
        self.cwe_root=get_cwe_root()
        # juliet对象,为juliet测试集和一些函数
        self.juliet=JULIET()
        self.get_result_from_xml_file()
        self.analyze_result()
        # 性能分析
        time_log_path='/home/pudding/work/temp/result/sast_result/sast_result/cppcheck/3/cppcheck_time.log'
        top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/cppcheck/3/cppcheck_top.log'
        iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/cppcheck/3/cppcheck_iotop.log'
        top_command='cppcheck'
        iotop_command='cppcheck'
        performance_save_path='./analyze_result/cppcheck_performance.xlsx'
        performance=PERFORMANCE(time_log_path,top_log_path,iotop_log_path,top_command,iotop_command,performance_save_path)

    def get_result_from_xml_file(self):
        '''根据cppcheck_result_xml_path生成cppcheck的结果到内存中'''
        tree=ET.parse(self.cppcheck_result_xml_path)
        root=tree.getroot()
        errors=root.findall('errors')[0].findall('error')
        for error in errors:
            id=error.attrib.get('id')
            if not error.attrib.get('cwe'):
                if self.id_cweid_dict.get(id):
                    cweid=self.id_cweid_dict[id]
                else:
                    print(f'在cppcheck的结果读取过程中,发现{id}没有cwe编号,请进行人工分析然后写入id_cweid_dict中')
            else:
                cweid='CWE-'+error.attrib['cwe']
            line_list=[]
            locations=error.findall('location')
            file_name=locations[0].attrib['file']
            for location in locations:
                file_name=location.attrib['file']
                line=location.attrib['line']
                line_list.append(line)
            symbols=error.findall('symbol')
            function_name_list=[]
            for symbol in symbols:
                function_name=symbol.text
                function_name_list.append(function_name)
            self.result.append({'cweid':cweid,'file_name':file_name,'line_list':line_list,'function_name_list':function_name_list})            
        
    def analyze_result(self):
        '''对result进行分析,生成最终分析结果'''
        # juliet测试用例的总数
        juliet_testcases_number=0
        for item in self.juliet.result.values():
            juliet_testcases_number+=len(item)
        # 测试成功的结果数量
        success_report_number=0
        # cppcheck重复报告相同的juliet测试用例数量
        cppcheck_repeat_number=0
        # cppcheck的结果总数
        cppcheck_result_number=len(self.result)
        # 匹配成功的juliet的测试用例列表，避免同一个测试用例被多次算作success_report_number里
        success_juliet_testcase_list=[]
        testnumber=0
        # 循环遍历result
        for item in self.result:
            # 根据文件名去juliet找相应的测试用例
            file_name=item['file_name'].split('/')[-1]
            juliet_testcase=self.juliet.get_testcase_by_file_name(file_name)
            # 如果找不到测试用例就continue
            if not juliet_testcase:
                continue
            # 获取juliet测试用例的cweid
            juliet_testcase_cweid=self.juliet.get_testcase_cweid(juliet_testcase)
            # 获取cppcheck与juliet的cweid的继承关系
            relations=self.cwe_root.get_relations(item['cweid'],juliet_testcase_cweid)
            # cppcheck为juliet的祖辈或相等或子辈
            if relations in ['ancestor','equal','descendant']:
                # 遍历cppcheck结果里的所有行数
                for line in item['line_list']:
                    # 行数与juliet测试结果的行数匹配
                    if self.juliet.if_mate_line(line,file_name,juliet_testcase):
                        # 如果juliet的该测试用例未被统计过,记作一次成功的报告,并记录下该测试用例
                        if juliet_testcase not in success_juliet_testcase_list:
                            success_report_number+=1
                            success_juliet_testcase_list.append(juliet_testcase)
                            break
                        # 如果juliet的该测试用例被统计过,记作一次重复报告
                        else:
                            cppcheck_repeat_number+=1
            # cppcheck与juliet无辈分关系,continue
            if relations=='unrelated':
                continue
        # 召回率/准确率：正确报告数与测试用例中用例总数的比值
        rcr=success_report_number/juliet_testcases_number
        # 误报率：错误报告数占工具所有报告总数的比值，错误报告数=cppcheck总报告数-cppcheck成功报告数-cppcheck重复报告数
        fpr=(cppcheck_result_number-success_report_number-cppcheck_repeat_number)/cppcheck_result_number
        excel_res={
            'juliet_testcases_number':[juliet_testcases_number],
            'cppcheck_result_number':[cppcheck_result_number],
            'success_report_number':[success_report_number],
            'rcr':[rcr],
            'fpr':[fpr],
        }
        df=pd.DataFrame(excel_res)
        df.to_excel('./analyze_result/cppcheck_analyze_result.xlsx', sheet_name='main')

class PERFORMANCE:
    '''负责对程序的性能进行分析,主要依据time.log top.log iotop.log文件来进行分析'''
    def __init__(self,time_log_path,top_log_path,iotop_log_path,top_command,iotop_command,save_path) -> None:
        # 记录程序开始和结束时间的time.log文件路径
        self.time_log_path=time_log_path
        # 记录cpu和mem数据的top.log文件路径
        self.top_log_path=top_log_path
        # 记录磁盘io数据的iotop.log文件路径
        self.iotop_log_path=iotop_log_path
        # 要记录的程序名
        self.top_command=top_command
        self.iotop_command=iotop_command
        # 程序总运行时间
        self.total_seconds=0
        self.get_total_seconds()
        # 用于解析top.log的内容
        self.top_block_list=[]
        self.get_top_block_list_from_top_log()
        # 解析top_block_list
        self.top_result=[]
        self.analyze_cpu_mem()
        # 用于解析iotop.log的内容
        self.iotop_block_list=[]
        self.get_iotop_block_list_from_iotop_log()
        # 解析iotop_block_list
        self.iotop_result=[]
        self.analyze_disk_io()
        # 生成最终excel数据
        self.save_path=save_path
        self.generate_result()

    def get_total_seconds(self):
        '''对time.log进行分析来获取总运行的时间'''
        with open(self.time_log_path,'r') as f:
            content=f.read()
        time_info=re.findall(r'(\d{2}:\d{2}:\d{2})',content,flags=0)
        start_time_str=time_info[0]
        end_time_str=time_info[1]
        start_time=datetime.strptime(start_time_str,'%H:%M:%S')
        end_time=datetime.strptime(end_time_str,'%H:%M:%S')
        self.total_seconds=(end_time-start_time).seconds

    def get_top_block_list_from_top_log(self):
        '''从top.log文件获取top_block_list'''
        # 读取 top 命令记录的文件
        with open(self.top_log_path) as f:
            lines = f.readlines()
        top_block_index_list=[]
        for i in range(len(lines)):
            if lines[i]=='\n' and i!=len(lines) and lines[i+1]=='    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND\n':
                top_block_index_list.append(i-5)
        for i in range(len(top_block_index_list)):
            index=top_block_index_list[i]
            if i!=len(top_block_index_list)-1:
                next_index=top_block_index_list[i+1]
            else:
                next_index=len(lines)
            top_block={'head':lines[index:index+5],'info':lines[index+7:next_index]}
            self.top_block_list.append(top_block)

    def analyze_cpu_mem(self):
        '''对top.log进行分析来获取cpu和内存占用相关信息'''
        for top_block in self.top_block_list:
            # 获取free数值，也就是第三行的id的数值
            heads=top_block['head']
            free=float(re.findall(r'(\d+\.\d+) id,',heads[2],flags=0)[0])
            infos=top_block['info']
            cpu=0
            mem=0
            
            df = pd.DataFrame([one_info.split() for one_info in infos], columns=['PID', 'USER', 'PR', 'NI', 'VIRT', 'RES', 'SHR', 'S', '%CPU', '%MEM', 'TIME+', 'COMMAND'])
            for i in range(len(df['PID'])):
                command=df['COMMAND'][i]    
                # TODO
                if command==self.top_command:
                    cpu+=float(df['%CPU'][i])
                    mem+=float(df['%MEM'][i])
            one_result={'cpu':cpu,'mem':mem,'free':free}
            self.top_result.append(one_result)

    def get_iotop_block_list_from_iotop_log(self):
        '''对iotop.log文件进行解析'''
        # 读取 iotop 命令记录的文件
        with open(self.iotop_log_path) as f:
            lines = f.readlines()
        iotop_block_index_list=[]
        for i in range(len(lines)):
            if lines[i]=='    TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND\n':
                iotop_block_index_list.append(i-2)
        for i in range(len(iotop_block_index_list)):
            index=iotop_block_index_list[i]
            if i!=len(iotop_block_index_list)-1:
                next_index=iotop_block_index_list[i+1]
            else:
                next_index=len(lines)
            iotop_block={'head':lines[index:index+2],'info':lines[index+3:next_index]}
            self.iotop_block_list.append(iotop_block)

    def analyze_disk_io(self):
        '''对iotop.log进行分析来获取磁盘io相关信息'''
        def translate_rate_to_float(rate):
            '''把4.72 M/s转换成4720000.0'''
            unit=re.findall(r'([KBMG])/s',rate,flags=0)[0]
            number=re.findall(r'(\d+\.\d+)',rate,flags=0)[0]
            if unit=='B':
                return float(number)
            if unit=='K':
                return float(number)*1000
            if unit=='M':
                return float(number)*1000*1000
            if unit=='G':
                return float(number)*1000*1000*1000

        for iotop_block in self.iotop_block_list:
            # 获取free数值，也就是第三行的id的数值
            heads=iotop_block['head']
            infos=iotop_block['info']
            io_read_total=0
            io_write_total=0
            for one_info in infos:
                if self.iotop_command in one_info:
                    read_write_tuple_list=re.findall(r'(\d+\.\d+ [KBMG]/s)\s+(\d+\.\d+ [KBMG]/s)\s+',one_info,flags=0)
                    io_read=read_write_tuple_list[0][0]
                    io_write=read_write_tuple_list[0][1]
                    io_read_total+=translate_rate_to_float(io_read)
                    io_write_total+=translate_rate_to_float(io_write)
            self.iotop_result.append({'read':io_read_total,'write':io_write_total})

    def generate_result(self):
        '''生成最终excel数据'''
        def translate_number_to_rate(number):
            '''把6530000.0转换成6.53Mb/s'''
            if number>1000:
                if number>1000*1000:
                    if number>1000*1000*1000:
                        unit='GB/s'
                        number=number/(1000*1000*1000)
                        return str(number)+unit
                    else:
                        unit='MB/s'
                        number=number/(1000*1000)
                        return str(number)+unit
                else:
                    unit='KB/s'
                    number=number/(1000)
                    return str(number)+unit
            else:
                unit='B/s'
                number=number
                return str(number)+unit
        
        cpu_total=0
        mem_total=0
        free_total=0
        read_total=0
        write_total=0
        for item in self.top_result:
            cpu_total+=item['cpu']
            mem_total+=item['mem']
            free_total+=item['free']
        cpu_average=cpu_total/len(self.top_result)
        mem_average=mem_total/len(self.top_result)
        free_average=free_total/len(self.top_result)
        cpu_average=str(cpu_average)+'%'
        mem_average=str(mem_average)+'%'
        free_average=str(free_average)+'%'
        for item in self.iotop_result:
            read_total+=item['read']
            write_total+=item['write']
        read_average=read_total/len(self.iotop_result)
        write_average=write_total/len(self.iotop_result)
        read_average=translate_number_to_rate(read_average)
        write_average=translate_number_to_rate(write_average)
        result={'cpu':[cpu_average],'mem':[mem_average],'free':[free_average],'read':read_average,'write':write_average,'time':self.total_seconds}
        df=pd.DataFrame(result)
        df.to_excel(self.save_path,sheet_name='result')

class MARK:
    '''对各项指标进行打分'''
    def __init__(self) -> None:
        self.infer=INFER()
        self.codeql=CODEQL()
        self.cppcheck=CPPCHECK()
        self.result={
            '指标名称':['准确性指标','准确性指标','性能指标','性能指标','性能指标','性能指标','性能指标'],
            '评分项目':['RCR/召回率/准确率(0.5)','FPR/误报率(0.5)','时间平均检出率(0.4)','CPU占用率(0.2)','内存占用率(0.2)','磁盘读占用(0.1)','磁盘写占用(0.1)'],
            'Infer':[],
            'CodeQL':[],
            'Cppcheck':[]
        }
        self.mark_accuracy()
        self.mark_performance()
        self.generate_result()

    def mark_accuracy(self):
        '''准确性指标打分'''
        infer_pd=pd.read_excel('./analyze_result/infer_analyze_result.xlsx',sheet_name='main')
        codeql_pd=pd.read_excel('./analyze_result/codeql_analyze_result.xlsx',sheet_name='main')
        cppcheck_pd=pd.read_excel('./analyze_result/cppcheck_analyze_result.xlsx',sheet_name='main')
        infer_rcr=infer_pd['rcr'][0]
        infer_fpr=infer_pd['fpr'][0]
        codeql_rcr=codeql_pd['rcr'][0]
        codeql_fpr=codeql_pd['fpr'][0]
        cppcheck_rcr=cppcheck_pd['rcr'][0]
        cppcheck_fpr=cppcheck_pd['fpr'][0]
        # RCR评分
        rcr_max=max(infer_rcr,codeql_rcr,cppcheck_rcr)
        infer_rcr_score=infer_rcr/rcr_max*100
        codeql_rcr_score=codeql_rcr/rcr_max*100
        cppcheck_rcr_score=cppcheck_rcr/rcr_max*100
        # FRP评分
        fpr_min=min(infer_fpr,codeql_fpr,cppcheck_fpr)
        infer_fpr_score=(1-infer_fpr)/(1-fpr_min)*100
        codeql_fpr_score=(1-codeql_fpr)/(1-fpr_min)*100
        cppcheck_fpr_score=(1-cppcheck_fpr)/(1-fpr_min)*100

        self.result['Infer'].append(infer_rcr_score)
        self.result['Infer'].append(infer_fpr_score)
        self.result['CodeQL'].append(codeql_rcr_score)
        self.result['CodeQL'].append(codeql_fpr_score)
        self.result['Cppcheck'].append(cppcheck_rcr_score)
        self.result['Cppcheck'].append(cppcheck_fpr_score)

    def mark_performance(self):
        '''性能指标打分'''
        def translate_rate_to_float(rate):
            '''把360.4KB/s转换成360400'''
            if 'GB/s' in rate:
                return float(rate[:-4])*1000*1000*1000
            if 'MB/s' in rate:
                return float(rate[:-4])*1000*1000
            if 'KB/s' in rate:
                return float(rate[:-4])*1000
            if 'B/s' in rate:
                return float(rate[:-3])
        
        infer_pd=pd.read_excel('./analyze_result/infer_performance.xlsx',sheet_name='result')
        codeql_pd=pd.read_excel('./analyze_result/codeql_performance.xlsx',sheet_name='result')
        cppcheck_pd=pd.read_excel('./analyze_result/cppcheck_performance.xlsx',sheet_name='result')
        infer_cpu=float(infer_pd['cpu'][0][:-1])
        infer_mem=float(infer_pd['mem'][0][:-1])
        infer_free=float(infer_pd['free'][0][:-1])
        infer_read=translate_rate_to_float(infer_pd['read'][0])
        infer_write=translate_rate_to_float(infer_pd['write'][0])
        infer_time=infer_pd['time'][0]
        codeql_cpu=float(codeql_pd['cpu'][0][:-1])
        codeql_mem=float(codeql_pd['mem'][0][:-1])
        codeql_free=float(codeql_pd['free'][0][:-1])
        codeql_read=translate_rate_to_float(codeql_pd['read'][0])
        codeql_write=translate_rate_to_float(codeql_pd['write'][0])
        codeql_time=codeql_pd['time'][0]
        cppcheck_cpu=float(cppcheck_pd['cpu'][0][:-1])
        cppcheck_mem=float(cppcheck_pd['mem'][0][:-1])
        cppcheck_free=float(cppcheck_pd['free'][0][:-1])
        cppcheck_read=translate_rate_to_float(cppcheck_pd['read'][0])
        cppcheck_write=translate_rate_to_float(cppcheck_pd['write'][0])
        cppcheck_time=cppcheck_pd['time'][0]
        infer_pd=pd.read_excel('./analyze_result/infer_analyze_result.xlsx',sheet_name='main')
        codeql_pd=pd.read_excel('./analyze_result/codeql_analyze_result.xlsx',sheet_name='main')
        cppcheck_pd=pd.read_excel('./analyze_result/cppcheck_analyze_result.xlsx',sheet_name='main')
        infer_success_report_number=infer_pd['success_report_number'][0]
        codeql_success_report_number=codeql_pd['success_report_number'][0]
        cppcheck_success_report_number=cppcheck_pd['success_report_number'][0]
        # 时间平均检出率评分
        infer_check_rate=infer_success_report_number/infer_time
        codeql_check_rate=codeql_success_report_number/codeql_time
        cppcheck_check_rate=cppcheck_success_report_number/cppcheck_time
        check_rate_max=max(infer_check_rate,codeql_check_rate,cppcheck_check_rate)
        infer_check_rate_score=infer_check_rate/check_rate_max*100
        codeql_check_rate_score=codeql_check_rate/check_rate_max*100
        cppcheck_check_rate_score=cppcheck_check_rate/check_rate_max*100
        # CPU占用率评分
        cpu_min=min(infer_cpu,codeql_cpu,cppcheck_cpu)
        infer_cpu_score=cpu_min/infer_cpu*100
        codeql_cpu_score=cpu_min/codeql_cpu*100
        cppcheck_cpu_score=cpu_min/cppcheck_cpu*100
        # 内存占用率评分
        mem_min=min(infer_mem,codeql_mem,cppcheck_mem)
        infer_mem_score=mem_min/infer_mem*100
        codeql_mem_score=mem_min/codeql_mem*100
        cppcheck_mem_score=mem_min/cppcheck_mem*100
        # 磁盘IO评分
        read_min=min(infer_read,codeql_read,cppcheck_read)
        infer_read_score=read_min/infer_read*100
        codeql_read_score=read_min/codeql_read*100
        cppcheck_read_score=read_min/cppcheck_read*100
        write_min=min(infer_write,codeql_write,cppcheck_write)
        infer_write_score=write_min/infer_write*100
        codeql_write_score=write_min/codeql_write*100
        cppcheck_write_score=write_min/cppcheck_write*100

        self.result['Infer'].append(infer_check_rate_score)
        self.result['Infer'].append(infer_cpu_score)
        self.result['Infer'].append(infer_mem_score)
        self.result['Infer'].append(infer_read_score)
        self.result['Infer'].append(infer_write_score)
        self.result['CodeQL'].append(codeql_check_rate_score)
        self.result['CodeQL'].append(codeql_cpu_score)
        self.result['CodeQL'].append(codeql_mem_score)
        self.result['CodeQL'].append(codeql_read_score)
        self.result['CodeQL'].append(codeql_write_score)
        self.result['Cppcheck'].append(cppcheck_check_rate_score)
        self.result['Cppcheck'].append(cppcheck_cpu_score)
        self.result['Cppcheck'].append(cppcheck_mem_score)
        self.result['Cppcheck'].append(cppcheck_read_score)
        self.result['Cppcheck'].append(cppcheck_write_score)

    def generate_result(self):
        '''生成最终评分excel文件'''
        df=pd.DataFrame(self.result)
        df.to_excel('./analyze_result/sast_result.xlsx',sheet_name='Sheet1')

def test():
    # infer=INFER()
    codeql=CODEQL()
    # cppcheck=CPPCHECK()
    # mark=MARK()
    pass

def find_function_at_line(filename, line_number):
    '''通过C文件名和行数来返回该行所在的函数名,当存在#include这些需要预编译器处理的代码时会报错,暂时还无法正常使用'''
    # 创建一个C语言解析器对象
    parser = c_parser.CParser()

    # 读取C语言源码文件
    with open(filename,encoding='utf-8') as f:
        code = f.read()

    # 解析C语言源码文件
    ast = parser.parse(code)

    # 遍历AST，查找包含指定行号的函数
    for node in ast.ext:
        if isinstance(node, c_ast.FuncDef):
            start_line = node.decl.coord.line
            end_line = node.body.coord.line
            if start_line <= line_number <= end_line:
                return node.decl.name.name

    # 如果没有找到包含指定行号的函数，返回None
    return None

test()
pass